home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / ffuncs.arc / GETBUILD.DOC < prev    next >
Encoding:
Text File  |  1988-09-03  |  20.0 KB  |  375 lines

  1.    This aritcle and accompanying programs were typed from the August 1988
  2. ( #142 ) issue of Dr. Dobbs Journal. The author & programmer is Marvin
  3. Hymowech, a contributing author for the magazine.  Any reference to wild-
  4. cards in the following text will not apply to the BLDFUNCS.EXE file in the 
  5. .ARC file. They won't work (as far as I know) unless you have a library 
  6. function supplied with Microsoft C and link with that function as explained 
  7. below.
  8.    Any function mentioned in the text below that begins with filter_ has
  9. been changed (in the code) to begin with flt_  so the program would work with 
  10. the Mix Power C debugger, which recognizes only the first 8 characters in 
  11. variables or functions. (not sure about variables)
  12. _________________________________________________________________________
  13.  
  14.                   ***** FIND THAT FUNCTION *****
  15.  
  16.    You're a C programmer who has just changed employers. You are suddenly
  17. responsible for maintaining more than 100 source code files and perhaps three 
  18. times as many C functions. In wandering through your new wealth of source
  19. code, you find a reference to a function called get_input(), which sounds 
  20. like a good thing to look at next. But which file is it in? Or perhaps you 
  21. recall seeing a function called save_screen() (or was it save_scrn() ?) that 
  22. would be just the thing to use in that new function you have to write, but you 
  23. just can't recall where you saw it. What to do?
  24.    The traditional way of handling these problems is this: First, you use a
  25. text-searching program such as GREP (Unix) or TS (from The Norton Utilities) 
  26. and search through all your source files for every reference to the function 
  27. you are looking for until you finally locate the reference that is the 
  28. function definition. Then, you bring up the source file in your editor. Next, 
  29. you search for the function again until you finally find the function 
  30. definition. By this time, the reason you were looking for it has usually 
  31. slipped your mind.
  32.    Having suffered through this situation several times over the years (and
  33. not just with other people's code), I resolved to find a better way. This 
  34. article presents my solution: a function finder for MS-DOS. Using the function 
  35. finder (actually, it consists of two programs) speeds up the process 
  36. considerably. You simply invode the finder with the function name as an 
  37. argument. The program then scans an index file, invokes your editor using the 
  38. appropriate source file, and, for sophisticated editors such as Brief, even 
  39. positions the cursor at the function definition.
  40.    The two programs that make up the function finder, BLDFUNCS and GETF, are 
  41. written in C. Although I used Microsoft C (Version 5.0) to compile the 
  42. programs presented here, the code could be ported to other compilers (or to 
  43. Unix) with minimal changes. The editor I use is Brief, Version 2.01, but any 
  44. other editor could be used.
  45.  
  46. HOW IT WORKS
  47.    As I mentioned earlier, my solution consists of two programs, BLDFUNCS
  48. and GETF.  BLDFUNCS constructs an index by reading through all the C source 
  49. files specified on the command line and then constructiog a text file 
  50. (FUNCS.TXT) that contains the name of each C source file read and a list of 
  51. all functions defined therein. The resulting file looks something like this:
  52.  
  53. file_1.c:
  54.       function_1a
  55.       function_1b
  56.       . . .
  57.       function_1z;
  58.  
  59. file_2.c:
  60.       function_2a
  61.       . . .
  62.  
  63.    Once the index file has been built by BLDFUNCS, GETF can be run with a
  64. function name as a command-line argument.  GETF reads FUNCS.TXT until it finds 
  65. a match for the specified function name (wildcard characters are allowed) and 
  66. then gets the name of the C source file in which the function resides. Next, 
  67. GETF constructs a DOS command line invoking the editor of your choice with 
  68. this source file as the file to be edited. Then, a DOS exec call is performed 
  69. to replace GETF by the editor in memory. For editors such as Brief, which let 
  70. you specify editor commands on the command line as well, even more is 
  71. possible. You can specify an appropriate search command to position the cursor 
  72. at an occurrence of the function name, which will hopefully be the actual 
  73. definition of the sought-for function.
  74.    In brief then, you run BLDFUNCS once to construct FUNCS.TXT and thereafter
  75. whenever major changes occur in the distribution of functions among your 
  76. source files. If your then need that elusive function SAVE_SCREEN 
  77. (SAVE_SCRN ?), you just type GETF SAVE_SCR*. One of three things will happen 
  78. then:
  79. 1. GETF will politely tell you that there is no such function in FUNCS.TXT.
  80. 2. GETF will find that there is exactly one function in FUNCS_TXT matching the
  81.    mask SAVE_SCR*, and the next thing you will see is the desired source 
  82.    file in your editor, with the cursor positioned at the exact place where
  83.    the function is defined (maybe).
  84. 3. GETF will find more than one match for SAVE_SCR* and will present you with 
  85.    a menu of such functions and the source files in which they are defined;
  86.    after you choose one of these files, it will be presented in the editor
  87.    as in case 2.
  88.  
  89.    BLDFUNCS es intended to read C files that work with almost any C compiler
  90. on the market; it is assumed, however, that the files will compile without 
  91. errors.
  92.  
  93. DEALING WITH EDITORS 
  94.    Now for some of the details. In an effort to land exactly at the function
  95. definition after the editor was invoked, I first tried instructing Brief to 
  96. start at the beginning of the file and search forward for the function name. 
  97. Most programmers, however, code so that functions are more likely to be refer-
  98. enced (in the source file in which they are defined) BEFORE they are defined 
  99. rather than after. In keeping with this general rule, I wrote a custom Brief 
  100. macro to start at the end of the file and search backward for the function 
  101. name instead. This worked much better and in fact lands me at the exact spot 
  102. about 50 percent of the time; when it doesn't work, I just use the key 
  103. assigned to SEARCH_AGAIN to continue looking.
  104.    The command line GETF uses to invoke the editor is built by using the DOS 
  105. environment variable GETFEDIT, which allows users to customize GETF to their 
  106. editor of choice. On my machine, my autoexec.bat file contains the line:
  107.  
  108.    set GETFEDIT=b -m'funcsrch %%s'%%s
  109.  
  110. Where funcsrch is the Brief macro mentioned earlier. The -m option tells Brief 
  111. to invoke the macro in quotes after it begins. GETF replaces the first %%s by 
  112. the function name it is looking for and the second %%s by the file name in 
  113. which it will be found. 
  114.    My Brief macro funcsrch is shown in Example 1, this page. As you can see, 
  115. Brief is programmed in a language that is roughly a cross between C and Lisp. 
  116. The effect of this macro is first to position the cursor at the end of the 
  117. file being edited and then to search backward for a specified string (the 
  118. function name in this instance). I found this aporoach was more effective for 
  119. positioning the cursor exactly on the definition of the sought-for function 
  120. rather than on a reference to it. The built-in macro end_of_buffer positions 
  121. the cursor at the end of the buffer and then the search_back macro is used to 
  122. look for the string s obtained from the command line invoking the macro. The 
  123. built-in external variable _s_pat is then set to the same value as s so that 
  124. subsequent invocations of the macro search_again (assigned to Shift-F5 on my 
  125. machine) will continue to search backward for the function name in question 
  126. (also note that the external variable _dir is set to 0 so that subsequent 
  127. search_again's will go backward, not forward).
  128.  
  129.    (macro funcsrch
  130.       (
  131.          ( string s )
  132.          ( extern _s_pat _dir center_line )
  133.  
  134.          ( get_parm 0 s )
  135.          ( message "locating function %s ..." s )
  136.          ( end_of_buffer )
  137.          (if ( > (search_back s) 0 )
  138.             (
  139.                ( message "function %s found" s )
  140.                ( center_line )
  141.                (sprintf _s_pat "%s" s )
  142.                ( = _dir 0 )
  143.             )
  144.          ;else
  145.             (
  146.                ( top_of_buffer )
  147.                ( beep )
  148.                ( message "function %s not found" s )
  149.             )
  150.          )
  151.       )
  152.    )
  153.  
  154.  
  155.    ***** EXAMPLE 1: A search macro for the Brief editor *****
  156.  
  157. BUILDING THE FUNCTION LIST
  158.    BLDFUNCS presented the thorniest problem: The program had to be suffic-
  159. iently cognizant of C syntax to extract the names of functions defined in a 
  160. source file yet ignore the similar constructions such as prototype function 
  161. declarations, used for argument type checking.
  162.    The approach I decided upon was to use several "filter functions" in suc-
  163. cession to simplify the character stream obtained from the source file until I 
  164. was able to extract the function names. Explaining from the bottom up (which 
  165. is essentially the order in which the program was coded), the lowest-level 
  166. filter is filter_cmt, which reads the raw character stream and returns a 
  167. character stream identical to its input except that all comments have been 
  168. eliminated. (Each filter function behaves like FGETC does - each takes a FILE 
  169. pointer as input and returns characters, the value EOF, or other special 
  170. values marking structures that have been "collapsed".) filter_cmt is 
  171. essentially a small finite-state machine, the states reflecting whether you 
  172. have just recieved an * (asterisk), or a / (slash), or neither. Some C 
  173. compilers allow nested comments, so filter_cmt maintains a cmt_level variable, 
  174. which is incremented when a /* (slash, asterisk) is received and decremented 
  175. when an */ (asterisk, slash) is received; characters are returned only when 
  176. cmt_level reaches zero.
  177.    The next filter is filter_quotes, which reads the character stream returned
  178. by filter_cmt and replaces any quoted string (delimited by either single or 
  179. double quotes) by the special value QUOTES, which is used as a place marker 
  180. for the original string. Higher-level filters look for matching curly braces, 
  181. for example, and would be confused by curly braces occurring within quotes. 
  182. The only subtlety in filter_quotes is to treat escape characters (preceded by 
  183. a backslash) correctly to avoid terminating a quoted string prematurely - for 
  184. example, the string \} (backslash, curly brace).
  185.    Next in the hierarchy comes filter_ppdir, whose task is to read from the
  186. stream provided by filter_quotes and eliminate all preprocessor directives. (I 
  187. have made the simplifying assumption that no function will be defined either 
  188. via a #define directive or in an include file. Although such peculiarities are 
  189. possible, they are rare and are not constructs found when good programming 
  190. style is employed.) The gotcha to be avoided in this filter is that #define 
  191. constructs may extend to several programming lines, so filter_ppdir is careful 
  192. to scan for escape new-line sequences (a backslash followed immediately by a 
  193. newline character) before deciding that a #define construct has terminated.
  194.    Next, filter_curly_braces reads the stream returned by filter_ppdir and 
  195. replaces all characters between matching curly brace pairs ( {} ) by the 
  196. special value BRACES. This is accomplished by maintaining a brace count that 
  197. begins at zero and is incremented when a left brace is encountered and decre-
  198. menter when a right brace is encountered. While the count is nonzero, no 
  199. incoming character is returned to the caller.
  200.  
  201. GETTING DOWN TO FUNCTIONS
  202.    At this point, the filtered input stream consists of external data items,
  203. function nodels for type checking, and actual function definitions. To 
  204. simplify the task further, filter_parens reads the character stream provided 
  205. by filter_curly_braces and eliminates all characters between parentheses, 
  206. returning instead the special value PARENS. This approach eliminates thorny 
  207. problems in the declaration of formal function parameters - for example, in 
  208. the function definition:
  209.  
  210. int function1(a, b, c)
  211. int(*a)();
  212. char (*b)();
  213. int c;
  214. {
  215. }
  216.  
  217.    Some data initialization expressions can have constructions that resemble
  218. functions also - for example:
  219.  
  220. int size = sizeof(array)/sizeof(element);
  221.  
  222.    To avoid such problems, filter_data reads the stream provided by
  223. filter_parens and deletes the right_hand side of any assignment and the equal 
  224. sign, leaving just the semicolon.
  225.    The stream returned by filter_data is sufficiently simple that it can now
  226. be used to extract the names of defined functions. The routine get_names_one_
  227. file opens the file specified by the parameter source_filename; reads this 
  228. file via filter_data; and writes the resulting function names to the file 
  229. specified by the parameter fp_out, which is the already opened FILE pointer to 
  230. the output stream for FUNCS.TXT. This is done by storing characters from 
  231. filter_data until EOF, or a semicolon, or the PARENS symbol is encountered. 
  232. You are really only interested in sequences like this that end with PARENS 
  233. because a sequence ending with a semicolon before a PARENS symbol cannot rep-
  234. resent a function definition.
  235.    The routine get_fn_name is now used to extract the function name from the
  236. stored line, by scanning backward from the PARENS symbol until a character is 
  237. encountered that is neither an underscore nor an alphanumeric. The decision as 
  238. to whether this is a function definition or a type-checking construction is 
  239. made as follows: If the first non-white-space character encountered after the 
  240. PARENS symbol is a semicolon or a comma, it is a type-checking construction. 
  241. The comma might occur as follows:
  242.  
  243. int func1(int, char), func2(int);
  244.  
  245. If you have an actual function definition, you bypass all characters until the 
  246. BRACES symbol is encountered because certainly no function definition can 
  247. occur between the PARENS and BRACES symbols. It only remains to append the 
  248. function name to the output file, FUNCS.TXT.
  249.  
  250. FINISHING OFF BLDFUNCS
  251.    The main function of BLDFUNCS opens FUNCS_TXT and calls get_names_one_file
  252. for every source file specified on the input line. To enable expansion of 
  253. wildcards on the command line, the module setargv (provided with the Microsoft 
  254. C compiler) is linked in with BLDFUNCS (See the MAKE file in Example 2). This 
  255. module lets you use a command such as:
  256.  
  257. bldfuncs *.c \othersource\*.c
  258.  
  259. which results in a FUNCS.TXT file containing the names of functions in every C 
  260. source file in both the current directory and the directory \othersource.
  261.  
  262.  
  263.    bldfuncs.obj:  bldfuncs.c
  264.       cl /c bldfuncs.c
  265.  
  266.    bldfuncs.exe:  bldfuncs.obj
  267.       link bldfuncs+\msc5\lib\setargv/ST:14000/NOE
  268.  
  269.  
  270.       ****** EXAMPLE 2: MAKE file for BLDFUNCS ******
  271.  
  272.    BLDFUNCS uses lots of stack space for local storage, so my MAKE file 
  273. specifies a stack size of 14000 in the link step:
  274.  
  275. link bldfuncs+\msc5\lib\setargv/ST:14000/NOE;
  276.  
  277. THE SECOND HALF
  278.    Now let's look at GETF, whose job is to scan FUNCS.TXT for a spedified
  279. function name (or for all names matching a specified pattern) and to present 
  280. the appropriate source file in the editor. As the first step in the process, 
  281. GETF looks for the DOS environment variable GETFEDIT, which must contain the 
  282. control string used to construct the command line that invokes the editor. For 
  283. example, if your editor were named edit. you would first issue the DOS 
  284. command:
  285.  
  286. set GETFEDIT=edit %s
  287.  
  288. and if this line were placed in your autoexec.bat file, it would look like:
  289.  
  290. set GETFEDIT=edit %s%s
  291.  
  292. Note the use of the %% sequence to avoid interpretation of the % symbol by 
  293. command.com
  294.    The Microsoft string function strstr is used to verify that there is at
  295. least one occurrence of %s in GETFEDIT. (Given two strings s1 and s2, 
  296. strstr(s1, s2) returns a character pointer to the first occurrence of s2 in s1 
  297. or NULL if there is none.) Then, the string function strtok is used to scan 
  298. GETFEDIT for an initial token (delimited by white space) that should be the 
  299. program name of the editor. Recall that given two strings, s and delim, 
  300. strtok(s, delim) scans s for a token ending with a character from the delim 
  301. string, replaces this character with a null, and returns a character pointer 
  302. to this token in s. Subsequent calls to strtok have the form strtok(NULL, 
  303. delim) and return character pointers to subsequent tokens, finally returning 
  304. NULL when no more tokens are to be had.
  305.    The program name of the editor is stored in pgm_name, and the remainder of
  306. the GETFEDIT string is stored in arg1_ctl; these are used later in construc-
  307. ting an exec call to invoke the editor.
  308.    Next, GETF opens FUNCS.TXT and scans for file names, which end with a 
  309. colon. Any such name is saved in file_token. Having obtained file_token, GETF 
  310. scans for function names that match the pattern func_name (obtained from the 
  311. GETF command line) using the function patn_match. Any such match is stored in 
  312. the arrays func_choices and (corresponding) file_choices indexed by the 
  313. integer num_choices.
  314.    When this scan terminates, num_choices is examined to see if any matches
  315. were found. If there was no match, an appropriate message is printed and the 
  316. editor is not invoked. If there was exactly one match, the function edit is 
  317. invoked to bring up the specified file and function in the editor. If several 
  318. matches occurred, the function ask_for_file is inviked to prompt the user for 
  319. a choice among these, and then the function edit is invoked as in the case of 
  320. one match.
  321.    The function patn_match accepts a pattern and a string as arguments and
  322. returns TRUE or FALSE, depending upon whether or not a match occurred. The
  323. pattern-matching rules are tailored to the case of function identifiers as 
  324. follows: a ?  matches any single character, an *  matches the remainder of any 
  325. string, and a %  matches any string up to the next underscore character or the 
  326. end of the string.
  327.    The function EDIT accepts a function and a file as parameters and uses
  328. the previously obtained strings pgm_name and arg1_ctl (parsed from the 
  329. GETFEDIT variable) as parameters for an execlp call, which, if successful, 
  330. will overlay the currently executing program, GETF, with the editor. The p in 
  331. execlp serves as a reminder that the DOS PATH variable is used to locate 
  332. pgm_name.
  333.    The function ask_for_file uses the arrays func_choices and file_choices
  334. and the index variable num_choices to present the user with a menu of possible 
  335. functions matching the pattern entered on the GETF command line and the corr-
  336. esponding files in which they reside. If the command getf get_%_data were 
  337. issued, a typical menu might look like:
  338.  
  339. Which one? (CR to exit)
  340.    1:get_all_data in getdata.c
  341.    2:get_good_data in valid.c
  342.    3:get_some_data in input.c
  343. Enter number:
  344.  
  345. When a listed number is chosen, the edit function is invoked using the 
  346. specified elements of func_choices and file_choices.
  347.    In order to avoid any stack-space problems, a generous allocation of 
  348. 14,000 stack bytes is made in the link step:
  349.  
  350. link getf/ST:14000;
  351.  
  352. The stack allocation of 14,000 bytes is the same for both BLDFUNCS and GETF.
  353.  
  354. SUMMING UP
  355.    I have found these two programs used in conjunction to be an excellent
  356. timesaver whenever I have to go wandering through source code files and have 
  357. been using them extensively since I wrote them. The programs have saved me a 
  358. great deal of time, and fall into the performance category of "fast enough." 
  359. Alhoough I've no doubt that there are more efficient ways of parsing through
  360. C source code than my multiple-filter strategy, the method I chose had two 
  361. outstanding advantages over other methods: it was easy to program, and it was 
  362. simple to debug.
  363.    Of course, the techniques presented in this article could be generalized to 
  364. languages other than C.  GETF would repuire almost no changes, but a new 
  365. version of BLDFUNCS would be repuired in order to do the parsing specific to 
  366. the language desired. I'd be interested in hearing from anyone who does the 
  367. conversion to another language.
  368.  
  369. Marvin Hymowech works as a programmer for Condor Computer Corp. Previously he 
  370. taught mathematics at the University of Michigan in Ann Arbor. He may be 
  371. reached at 4906 Cole Blvd., Ypsilanti, MI 48197.
  372.  
  373.                ******* END OF TEXT *******
  374.  
  375.